3 // HigherOrderMessaging
5 // Created by Ofri Wolfus on 26/08/06.
6 // Copyright 2006 Ofri Wolfus. All rights reserved.
10 #include <objc/objc-runtime.h>
11 #include <malloc/malloc.h>
12 //#import "HOMUtilities.h"
13 //#include "DPObjC-Compatibility.h"
14 #import <HigherOrderMessaging/DPObjCRuntime.h>
15 #import <Foundation/NSInvocation.h>
16 //#import "HOMInvocationBuilder.h"
20 * This file is the base of @HOM.
21 * It contains the machanism that turns a Objective-C message into a Message instance,
22 * and the entire framework relies on this.
23 * NOTE: This is not possible without the ID() macro that tricks GCC to think the MSG() macro
28 id _sharedMessageBuilder = nil; // No longer used
30 @interface Message (MessageBuilderSupport)
34 #define _returnesStruct(list) (marg_getValue(list, 0, void *) != _sharedMessageBuilder)
36 @implementation Message
38 static Class autoreleasePoolCls = Nil;
41 autoreleasePoolCls = objc_getClass("NSAutoreleasePool");
44 + (id)_messageWithArgs:(marg_list)list method:(MethodDescription)desc {
45 Message *m = class_createInstance(self, 0);
48 size_t len = strlen(description_getTypes(desc));
49 char *types = calloc(len+1, sizeof(char));
51 memcpy(types, description_getTypes(desc), len * sizeof(char));
55 //marg_setValue(m->args, (_returnesStruct(list) ? sizeof(void *) : 0), id, nil);
56 m->argsSize = description_getSizeOfArguments(desc); // We cache the size of the method in order to speed up some HOM implementations
57 m->description = description_createDescription(description_getName(desc), types);
60 return [m autorelease];
65 free((void *)description_getTypes(description)); // We copied the types at initialization time
71 return marg_getValue(args, (_returnesStruct(args) ? sizeof(void *) : 0) + sizeof(id), SEL);
74 - (marg_list)arguments {
78 - (unsigned)argumentsSize {
82 - (BOOL)returnsStruct {
83 return _returnesStruct(args);
86 - (unsigned)numberOfArguments {
87 #define hom_getNumberOfArguments(s) sel_getNumberOfArguments(s)
88 return hom_getNumberOfArguments([self selector]);
91 - (const char *)types {
92 return description_getTypes(description);
95 - (MethodDescription)methodDescription {
99 - (id)forward:(SEL)sel :(marg_list)arglist {
109 if (autoreleasePoolCls)
110 [autoreleasePoolCls addObject:self];
122 - (unsigned)retainCount {
126 - (NSString *)description {
127 return [NSString stringWithFormat:@"<0x%x> Message: \"%s\"", self, sel_getName([self selector])];
130 - (id)sendTo:(id)receiver {
131 return objc_msgSendv(receiver, [self selector], argsSize, args);
136 @implementation Message (CocoaConventions)
138 - (id)invocationWithTarget:(id)target {
140 id builder = [HOMInvocationBuilder builderForTarget:target];
142 r = [self sendTo:builder];
150 //=================================================================================//
151 //=================================================================================//
152 //=================================================================================//
154 // Finds a method description for the given selector, which has the largest arguments size.
155 // The returned description must be freed properly.
156 MethodDescription _hom_findMethodForSelector(SEL sel) {
157 // Holds all our cached method descriptions
158 static CFMutableDictionaryRef _methodsCache = NULL;
160 static int classCount = 0; // The number of classes that was in our previous search
161 static Class *classes = NULL; // A block with pointers to all classes
163 MethodDescription result;
165 NSString *key = NSStringFromSelector(sel);
167 // Like in the ObjC runtime, thread synchronization is always active
168 @synchronized ((NSDictionary *)_methodsCache) {
169 // Set up our cache dictionary if it wasn't already active
171 _methodsCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
173 // Look for a cached version of our method.
174 // If no new classes were registered since the last time we did a search, this method will be used
175 result = (MethodDescription)CFDictionaryGetValue(_methodsCache, key);
177 // Get the current number of classes registered with the runtime
178 count = objc_getClassList(NULL, 0);
181 * We work only if new classes were registered since our last search.
182 * NOTE: When unloading of classes is implemened this check may not be enough
183 * if some classes were added, and some others were removed but the total number of classes remained the same.
184 * Even in this situation though, the chances are very small the new classes added new method for existing selector(s)
185 * with a different size of total arguments.
187 if (count != classCount) {
188 // Allocate enough memory for pointers to all classes available
190 classes = malloc(sizeof(Class) * count);
192 classes = reallocf(classes, sizeof(Class) * count);
195 // Remember the current number of classes
198 // Get all available classes from the runtime
199 objc_getClassList(classes, classCount);
201 // Clean up our cache
202 CFDictionaryRemoveAllValues(_methodsCache);
205 // Free our previous cached method
207 //free(result->method_types);
217 unsigned argsSize = 0U;
220 * Loop through all classes and search for a method with a matching selector
221 * This is a very long loop and that's why we use our cache if possible
222 * NOTE: Since method lookups scans through all super classes, we can cut down the number of lookups
223 * by remembering which super classes were already searched and skip those.
225 for (i = 0; i < classCount; i++) {
228 // First, attempt to get an instance method
229 m = class_getInstanceMethod(cls, sel);
231 // If it's not available, try a class method
233 m = class_getClassMethod(cls, sel);
236 * If we found a method, we need to get the size of its arguments.
237 * Since we can't know which method will be used in practice, we'll pick the one with the largest
238 * size so that we don't accidentally cut some arguments.
241 unsigned s = method_getSizeOfArguments(m);
251 //unsigned len = strlen(meth->method_types);
253 // Allocate a new method, and copy the method we just found.
254 // NOTE: The IMP of our method always points to NULL.
255 /*result = calloc(1, sizeof(struct objc_method));
256 result->method_name = sel;
257 result->method_types = malloc(sizeof(char) * (len + 1));
258 strcpy(result->method_types, meth->method_types);*/
259 result = dp_copyMethodDescription(meth);
261 // Cache our method description
262 CFDictionarySetValue(_methodsCache, key, result);
270 //=================================================================================//
271 //=================================================================================//
272 //=================================================================================//
275 * The MessageBuilder class is a special class that responds to has no methods.
276 * Every message sent to it will be packed and returned as a Message instance.
277 * Unlike normal singleton objects, there is no instance of this class and messages will be sent
278 * to the class directly.
280 @interface MessageBuilder {
285 @implementation MessageBuilder
287 // Set up the public pointer to ourself
289 _sharedMessageBuilder = self;
292 // The runtime automatically calls this method, so we make sure it won't get accidentally forwarded.
296 // Catche all messages and turn them into Message instances
297 - (id)forward:(SEL)sel :(marg_list)args {
299 MethodDescription d = _hom_findMethodForSelector(sel);
302 //marg_malloc(list, m);
303 list = dp_marg_malloc(description_getSizeOfArguments(d));
304 memcpy(list, args, malloc_size(list));
306 return [Message _messageWithArgs:list method:d];